////
//
/// \file   ./src/expression_node.cpp
//
//  Copyright 2007, 2008 Chandler Carruth
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations
//  under the License.
//
////

#include <inc/parser/expression_node.hpp>

#include <string>

#include <boost/scoped_ptr.hpp>

#include <inc/parser/function_call_node.hpp>
#include <inc/parser/immediate_node.hpp>
#include <inc/parser/operation_node.hpp>
#include <inc/parser/name_node.hpp>
#include <inc/parser/parse_tree_node.hpp>

namespace inc {
namespace parser {

using std::string;
using boost::scoped_ptr;

/// \brief  Parse an expression
/// \author Chandler Carruth
/// \date   2007.06.27
///
/// This parses out an expression, selectively handing off parsing to the
/// various sub nodes until a successful parse is made.

bool
ExpressionNode::parse( const iterator& first,
                       const iterator& last,
                       iterator& last_parsed )
{
  assert( get_text().empty() && !node && !self_reference
          && "This node was parsed more than once!" );

  //  The easiest to parse expression is the literal "self", check that first.
  if( distance(first, last) >= 4 && string(first, first + 4) == "self" )
  {
    set_text( "self" );
    self_reference = true;
    last_parsed = first + 4;
    return true;
  }

  boost::scoped_ptr<ParseTreeNode> imm( new ImmediateNode );
  if( imm->parse( first, last, last_parsed ) )
  {
    set_text( imm->get_text() );
    node.swap( imm );
    return true;
  }

  //  We parse function calls next because all function calls begin with
  //  a valid name, but are followed by a () pair.
  //
  //  TODO(chandlerc): It would be really nice to re-structure this so we parse
  //  a name, and then try to parse the call using that name. As it currently
  //  stands, this will be one of the worst points for back-tracking: every
  //  name will be parsed twice.
  boost::scoped_ptr<ParseTreeNode> func( new FunctionCallNode );
  if( func->parse( first, last, last_parsed ) )
  {
    set_text( func->get_text() );
    node.swap( func );
    return true;
  }

  //  Try parsing a name next, as the possible syntaxes for a name are both
  //  simpler (and therefore more likely to fail fast), and cheaper to parse
  //  than operations.
  boost::scoped_ptr<ParseTreeNode> name( new NameNode );
  if( name->parse( first, last, last_parsed ) )
  {
    set_text( name->get_text() );
    node.swap( name );
    return true;
  }

  boost::scoped_ptr<ParseTreeNode> operation( new OperationNode );
  if( operation->parse( first, last, last_parsed ) )
  {
    set_text( operation->get_text() );
    node.swap( operation );
    return true;
  }

  last_parsed = first;
  return false;
}

/// \brief  Convenience getter for a Name sub-node
/// \author Chandler Carruth
/// \date   2008.05.19

NameNode*
ExpressionNode::get_name() const
{
  return dynamic_cast<NameNode*>( node.get() );
}

/// \brief  Convenience getter for an immediate value sub-node
/// \author Chandler Carruth
/// \date   2008.05.24

ImmediateNode*
ExpressionNode::get_immediate() const
{
  return dynamic_cast<ImmediateNode*>( node.get() );
}

/// \brief  Convenience getter for an function call sub-node
/// \author Chandler Carruth
/// \date   2008.05.24

FunctionCallNode*
ExpressionNode::get_function_call() const
{
  return dynamic_cast<FunctionCallNode*>( node.get() );
}

/// \brief  Convenience getter for an operation sub-node
/// \author Chandler Carruth
/// \date   2008.05.24

OperationNode*
ExpressionNode::get_operation() const
{
  return dynamic_cast<OperationNode*>( node.get() );
}

} //  end parser namespace
} //  end inc namespace
